#include "mprpcprovider.h"
#include "rpcheader.pb.h"
#include "mprpcapplication.h"
#include <fstream> 
#include <unistd.h>              // gethostname()
#include <netdb.h>               // hostent结构体、gethostbyname()
#include <arpa/inet.h>           // in_addr结构体、inet_ntoa()（IP地址转换）

namespace rpc {

// 发布rpc服务方法接口
void MprpcProvider::NotifyService(google::protobuf::Service* service) {
    ServiceInfo service_info;

    // 获取服务对象的信息
    const google::protobuf::ServiceDescriptor* pserviceDesc =
        service->GetDescriptor();
    // 获取服务的名字
    std::string service_name = pserviceDesc->name();
    // 获取服务对象的方法数量
    int methodCnt = pserviceDesc->method_count();

    std::cout << "service_name:" << service_name << std::endl;

    for (int i = 0; i < methodCnt; ++i) {
        // 获取服务对象指定下标的服务方法描述
        const google::protobuf::MethodDescriptor* pmethodDesc = pserviceDesc->method(i);
        std::string method_name = pmethodDesc->name();
        service_info.m_methodMap.insert(std::make_pair(method_name, pmethodDesc));

        std::cout << "method_name:" << method_name << std::endl;
    }
    service_info.m_service = service;
    m_serviceMap.insert(std::make_pair(service_name, service_info));
}

// 启动rpc节点
void MprpcProvider::Run(int nodeIndex, short port) {
    //获取可用ip
    char *ipC;
    char hname[128];
    struct hostent *hent;
    gethostname(hname, sizeof(hname));  // 获取本机主机名（如"localhost"或主机名）
    hent = gethostbyname(hname);        // 根据主机名获取主机网络信息（包含IP地址列表）
    for (int i = 0; hent->h_addr_list[i]; i++) {
        ipC = inet_ntoa(*(struct in_addr *)(hent->h_addr_list[i]));  // 将网络字节序IP转为字符串
    }
    std::string ip = std::string(ipC);  // 存储最后一个有效IP地址（可能是多个网卡/IP中的最后一个）

    //写入文件 "test.conf"
    std::string node = "node" + std::to_string(nodeIndex);
    std::ofstream outfile;
    outfile.open("test.conf", std::ios::app);  //打开文件并追加写入
    if (!outfile.is_open()) {
        std::cout << "打开文件失败！" << std::endl;
        exit(EXIT_FAILURE);
    }
    outfile << node + "ip=" + ip << std::endl;
    outfile << node + "port=" + std::to_string(port) << std::endl;
    outfile.close();

    // 服务器地址和端口号
    muduo::net::InetAddress address(ip, port);

    // 创建TcpServer对象
    muduo::net::TcpServer server(&m_eventloop, address, "RpcProvider");

    // 设置回调函数
    server.setConnectionCallback(
        std::bind(&MprpcProvider::OnConnection, this, std::placeholders::_1));
    server.setMessageCallback(
        std::bind(&MprpcProvider::OnMessage, this, std::placeholders::_1,
                    std::placeholders::_2, std::placeholders::_3));

    // 设置工作线程数量
    server.setThreadNum(4);

    // rpc服务端准备启动，打印信息
    std::cout << "RpcProvider start service at ip:" << ip << " port:" << port << std::endl;

    // 启动网络服务
    server.start();
    m_eventloop.loop();
}

// 已连接的用户读事件回调，来了rpc服务请求就响应
/*
 * provider 和 consumer 需要协商好之间传递的数据格式
 * server_name method_name args 利用protobuf进行数据头的序列化和反序列化
 * header_size(4个字节) + header_str(服务对象和方法) + args_str(调用方法的参数) 
 */
void MprpcProvider::OnConnection(const muduo::net::TcpConnectionPtr &conn) {
    // 如果是新连接就什么都不干，即正常的接收连接即可
    if (!conn->connected()) {
        // 和rpc client的连接断开了
        conn->shutdown();
    }
}

void MprpcProvider::OnMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp receive_time) {
    // 网络上接收的远程rpc调用请求的字符流    Login args
    std::string recv_buf = buf->retrieveAllAsString();
  
    uint32_t header_size = 0;
    recv_buf.copy((char*)&header_size, 4, 0);
  
    // 根据header_size读取数据头的原始字符流，反序列化数据，得到rpc请求的详细信息
    std::string rpc_header_str = recv_buf.substr(4, header_size);
    rpc::RpcHeader rpcHeader;
    std::string service_name;
    std::string method_name;
    uint32_t args_size;

    // 解析数据头
    if (rpcHeader.ParseFromString(rpc_header_str)) {
        // 数据头反序列化成功
        service_name = rpcHeader.service_name();
        method_name = rpcHeader.method_name();
        args_size = rpcHeader.args_size();
    } 
    else {
        // 数据头反序列化失败
        std::cout << "rpc_header_str:" << rpc_header_str << " parse error!" << std::endl;
        return;
    }
  
    // 获取rpc方法参数的字符流数据
    std::string args_str = recv_buf.substr(4 + header_size, args_size);
  
    // 打印调试信息
    //    std::cout << "============================================" << std::endl;
    //    std::cout << "header_size: " << header_size << std::endl;
    //    std::cout << "rpc_header_str: " << rpc_header_str << std::endl;
    //    std::cout << "service_name: " << service_name << std::endl;
    //    std::cout << "method_name: " << method_name << std::endl;
    //    std::cout << "args_str: " << args_str << std::endl;
    //    std::cout << "============================================" << std::endl;
  
    // 获取service对象和method对象
    auto it = m_serviceMap.find(service_name);
    if (it == m_serviceMap.end()) {
        std::cout << "服务：" << service_name << " is not exist!" << std::endl;
        std::cout << "当前已经有的服务列表为:";
        for (auto item : m_serviceMap) {
            std::cout << item.first << " ";
        }
        std::cout << std::endl;
        return;
    }
  
    auto mit = it->second.m_methodMap.find(method_name);
    if (mit == it->second.m_methodMap.end()) {
        std::cout << service_name << ":" << method_name << " is not exist!" << std::endl;
        return;
    }
  
    google::protobuf::Service* service = it->second.m_service;       // 获取service对象  new UserService
    const google::protobuf::MethodDescriptor *method = mit->second;  // 获取method对象  Login
  
    // 生成rpc方法调用的请求request和响应response参数，由于是rpc的请求，因此请求需要通过request来序列化
    google::protobuf::Message* request = service->GetRequestPrototype(method).New();
    if (!request->ParseFromString(args_str)) {
        std::cout << "request parse error, content:" << args_str << std::endl;
        return;
    }
    google::protobuf::Message* response = service->GetResponsePrototype(method).New();
  
    // 给下面的method方法的调用，绑定一个Closure的回调函数
    // closure是执行完本地方法之后会发生的回调，因此需要完成序列化和反向发送请求的操作
    google::protobuf::Closure *done = google::protobuf::NewCallback<MprpcProvider, 
                                                                    const muduo::net::TcpConnectionPtr&, 
                                                                    google::protobuf::Message*>(
                                                                    this, &MprpcProvider::SendRpcResponse, conn, response);
  
    // 在框架上根据远端rpc请求，调用当前rpc节点上发布的方法
    service->CallMethod(method, nullptr, request, response, done);
}

// Closure的回调操作，用于序列化rpc的响应和网络发送,发送响应回去
void MprpcProvider::SendRpcResponse(const muduo::net::TcpConnectionPtr &conn, google::protobuf::Message *response) {
    std::string response_str;
    if (response->SerializeToString(&response_str))  // response进行序列化
    {
        // 序列化成功后，通过网络把rpc方法执行的结果发送会rpc的调用方
        conn->send(response_str);
    } 
    else {
        std::cout << "serialize response_str error!" << std::endl;
    }
    // conn->shutdown(); // 模拟http的短链接服务，由rpcprovider主动断开连接  //改为长连接，不主动断开
}

}   // namespace rpc